package com.lemon.springcloud.core.initial;


import com.lemon.springcloud.core.bean.ServiceInfo;
import com.lemon.springcloud.core.component.zookeeper.ZkPropKeyListener;
import com.lemon.springcloud.core.component.zookeeper.ZkRegister;
import com.lemon.springcloud.core.config.LemonSpringBootConfig;
import com.lemon.springcloud.core.listener.ShutdownListener;
import com.lemon.springcloud.core.listener.def.LemonStarterListeners;
import com.lemon.springcloud.core.listener.def.LemonZkPropertiesListener;
import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.SpringBootVersion;
import org.springframework.boot.ansi.AnsiColor;
import org.springframework.boot.ansi.AnsiOutput;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;

/**
 * @author hdzhang
 * @ClassName LemonApplicationInitializer
 * @desc
 * @Date 2018/9/7 19:03
 * @Version 1.0
 **/
public class LemonApplicationInitializer {

    private static LemonSpringBootConfig lemonSpringBootConfig = null;

    /**
     * 控制台 banner
     */
    private static final String BANNER_TEXT;

    /**
     * 启动打印信息
     */
    private static String STARTER_INFO_PRINT_DATA;

    /**
     * 是否启用 lemonApplicationInitializerEnable
     */
    private static Boolean lemonApplicationInitializerEnable = false;

    /**
     * 是否启用 mybatis
     */
    private static Boolean lemonBuildMybatisEnable = false;

    /**
     * 是否启用 redis
     */
    private static Boolean lemonBuildRedisEnable = false;
    /**
     * 是否启用 rabbitmq
     */
    private static Boolean lemonBuildRabbitMQEnable = false;

    /**
     * 是否启用zookeeper
     */
    private static Boolean lemonBuildInZkEnable = false;

    private static boolean zkConnected;

    private static ZkRegister zkRegister;

    /**
     * 是否启用 es
     */
    private static Boolean lemonBuildInEsEnable = false;

    private static LemonZkPropertiesListener lemonZkPropertiesListener = null;


    /**
     * 启动服务信息纪录类
     */
    private static ServiceInfo serviceInfo;

    private LemonStarterListeners lemonStarterListeners;

    private static final int PAD_SIZE = 40;

    static {
        // Initialize LEMON starter banner.
        InputStream bannerStream = LemonApplicationInitializer.class.getClassLoader().getResourceAsStream("lemon_banner.txt");
        BufferedReader reader = new BufferedReader(new InputStreamReader(bannerStream));
        String line;
        StringBuilder builder = new StringBuilder();
        try {
            while ((line = reader.readLine()) != null) {
                builder.append("| ").append(line).append("\n");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        lemonSpringBootConfig = new LemonSpringBootConfig();

        // Fill banner variable.
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy.MM.dd HH:mm");
        BANNER_TEXT = builder.toString()
                .replaceAll("\\( v", "(" + dateFormat.format(new Date()) + " v");
    }

    /**
     * 打印服务应用的基本启动信息
     *
     * @param info 需要打印的服务信息
     * @return 信息
     */
    public final String printAppStartInfo(ServiceInfo info) {
        String separator = "=======================================================";
        AtomicReference<StringBuilder> infoBuilder = new AtomicReference<>(new StringBuilder());
        infoBuilder.get().append(separator).append(" Lemon Service/App Starter Info ").append(separator)
                .append("\n");
        infoBuilder.get().append(BANNER_TEXT);

        infoBuilder.get().append(StringUtils.rightPad("| Spring Application Name", PAD_SIZE))
                .append("[").append(ServiceInfo.getApplicationName()).append("]").append("\n");
        infoBuilder.get().append(StringUtils.rightPad("| Server IP", PAD_SIZE)).append(info.getIp())
                .append(info.getDevHost() == null ? "" : "\t[DEVELOPER:" + info.getDevHost())
                .append(info.getDevDesc() == null ? "" : " - " + info.getDevDesc() + "]")
                .append("\n");
        infoBuilder.get().append(StringUtils.rightPad("| Server PORT", PAD_SIZE)).append(info.getServerPort()).append("\n");
        infoBuilder.get().append(StringUtils.rightPad("| Service/App PID", PAD_SIZE)).append(info.getPid()).append("\n");
        infoBuilder.get().append(StringUtils.rightPad("| Service UUID", PAD_SIZE)).append(ServiceInfo.getServiceUUID()).append("\n");

        String tag = "A".equals(info.getServiceTag()) ? "A (Def)" : info.getServiceTag();
        infoBuilder.get().append(StringUtils.rightPad("| Service/App Tag", PAD_SIZE)).append(tag).append("\n");

        infoBuilder.get().append(StringUtils.rightPad("| Java Runtime", PAD_SIZE)).append(info.getJavaRuntime()).append("\n");
        infoBuilder.get().append(StringUtils.rightPad("| Service/App Start Time", PAD_SIZE)).append(new Date(info.getStartTime())).append("\n");
        infoBuilder.get().append(StringUtils.rightPad("| Spring Boot Version", PAD_SIZE)).append(SpringBootVersion.getVersion()).append("\n");
        infoBuilder.get().append(StringUtils.rightPad("| Spring Profiles Active", PAD_SIZE))
                .append(System.getProperty("spring.profiles.active", "default")).append("\n");
        infoBuilder.get().append(StringUtils.rightPad("| Had Shutdown Listener Registered", PAD_SIZE)).append(info.getShutdownListenerRegistered()).append("\n");
        infoBuilder.get().append(StringUtils.rightPad("| Had Registered Notifier", PAD_SIZE)).append(info.getRegisterNotificationHook()).append("\n");
        infoBuilder.get().append("| ").append("\n");

        Map<String, Object> extStartInfo = this.pluginDetector();
        if (extStartInfo.size() > 0) {
            infoBuilder.get().append(StringUtils.rightPad("| INSTALLED LEMON SPRING BOOT STARTERS:", PAD_SIZE))
                    .append(lemonStarterListeners.plugins().size()).append("\n");
        }
        Map<String, Object> customExt = extStartInfo();
        if (customExt != null) {
            extStartInfo.putAll(customExt);
        }
        if (extStartInfo.size() > 0) {
            infoBuilder.get().append("|-----------------------------------------------------------\n");
            for (String key : extStartInfo.keySet()) {
                infoBuilder.get().append("| ").append(StringUtils.rightPad(key + ":", PAD_SIZE + 5)).append("\t").append(extStartInfo.get(key)).append("\n");
            }
            infoBuilder.get().append("|\n");
        }
        infoBuilder.get().append(String.format("| Initialization completed in %s ms.\t", info.getStarterDuration())).append("\n");
        infoBuilder.get().append(separator).append(" Lemon Service/App Starter Info ").append(separator);
        STARTER_INFO_PRINT_DATA = infoBuilder.toString();
        return AnsiOutput.toString(AnsiColor.GREEN, STARTER_INFO_PRINT_DATA, AnsiColor.DEFAULT);
    }

    private Map<String, Object> pluginDetector() {
        Map<String, Object> data = new LinkedHashMap<String, Object>();

        Map<String, Map<String, Object>> plugins = lemonStarterListeners.plugins();
        int cnt = 1;
        for (String pluginName : plugins.keySet()) {
            data.put((cnt++) + "). " + pluginName.toUpperCase(), "");
            Map<String, Object> pluginInfo = plugins.get(pluginName);
            if (pluginInfo != null && pluginInfo.size() > 0) {
                for (String infoKey : pluginInfo.keySet()) {
                    data.put("\t + " + infoKey.toUpperCase(), pluginInfo.get(infoKey));
                }
            }
        }
        return data;
    }

    /**
     * 打印服务应用的基本停止信息
     *
     * @param info 需要打印的停止信息
     * @return 信息
     */
    public final String printAppEndInfo(ServiceInfo info) {
        AtomicReference<StringBuilder> infoBuilder = new AtomicReference<>(new StringBuilder());
        infoBuilder.get().append("======================================== Lemon Service/App Stopped Info ========================================")
                .append("\n");

        infoBuilder.get().append("| Spring Application Name:\t[").append(info.getApplicationName()).append("]\n");
        infoBuilder.get().append("| Server PORT:\t").append(info.getServerPort()).append("\n");
        infoBuilder.get().append("| Service/App PID:\t").append(info.getPid()).append("\n");
        infoBuilder.get().append("| Service/App Tag:\t").append(info.getServiceTag()).append("\n");
        infoBuilder.get().append("| Java Runtime:\t").append(info.getJavaRuntime()).append("\n");
        infoBuilder.get().append("| Service/App Start Time:\t").append(new Date(info.getStartTime())).append("\n");
        infoBuilder.get().append("| Service/App Stopped Time:\t").append(new Date(info.getEndTime())).append("\n");
        infoBuilder.get().append("| Spring Profiles Active:\t")
                .append(System.getProperty("spring.profiles.active", "default")).append("\n");
        infoBuilder.get().append("| Had Registered Shutdown Listener:\t").append(info.getShutdownListenerRegistered()).append("\n");
        infoBuilder.get().append("| Had Registered Notifier:\t").append(info.getRegisterNotificationHook()).append("\n");

        BigDecimal duration = new BigDecimal((info.getEndTime() - info.getStartTime()) / 60000.0);
        infoBuilder.get()
                .append("| Running Duration:\t")
                .append(duration.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue())
                .append(" min").append("\n");

        infoBuilder.get().append("| ").append("\n");
        Map<String, Object> extEndInfo = extEndInfo();
        if (extEndInfo != null && extEndInfo.size() > 0) {
            infoBuilder.get().append("|---------\n");
            for (String key : extEndInfo.keySet()) {
                infoBuilder.get().append("| ").append(key).append(":\t").append(extEndInfo.get(key)).append("\n");
            }
        }
        infoBuilder.get().append(BANNER_TEXT);
        infoBuilder.get().append("======================================== Lemon Service/App Stopped Info ========================================");
        return AnsiOutput.toString(AnsiColor.BRIGHT_RED, infoBuilder.toString(), AnsiColor.DEFAULT);
    }

    /**
     * 配置服务/应用停止监听器，一般用于在结束一个应用或者服务的时候，等待未完成的任务或服务执行完成，或者补充持久化数据等防止数据丢失的
     * 回调
     *
     * @return ShutdownListener 停止监听器的实现类
     */
    public ShutdownListener getShutdownListener() {
        return null;
    }

    /**
     * 扩展的服务启动信息
     *
     * @return 启动键值信息，Override
     */
    public Map<String, Object> extStartInfo() {
        return null;
    }

    /**
     * 扩展的服务结束信息
     *
     * @return 停止键值信息，Override
     */
    public Map<String, Object> extEndInfo() {
        return null;
    }

    public LemonStarterListeners getLemonStarterListeners() {
        return lemonStarterListeners;
    }

    public void setLemonStarterListeners(LemonStarterListeners lemonStarterListeners) {
        this.lemonStarterListeners = lemonStarterListeners;
    }

    public static LemonSpringBootConfig getLemonSpringBootConfig() {
        return lemonSpringBootConfig;
    }

    public static void setLemonSpringBootConfig(LemonSpringBootConfig lemonSpringBootConfig) {
        LemonApplicationInitializer.lemonSpringBootConfig = lemonSpringBootConfig;
    }

    public static Boolean isLemonApplicationInitializerEnable() {
        return lemonApplicationInitializerEnable;
    }

    public static void setLemonApplicationInitializerEnable(Boolean lemonApplicationInitializerEnable) {
        LemonApplicationInitializer.lemonApplicationInitializerEnable = lemonApplicationInitializerEnable;
    }

    public static Boolean getLemonBuildMybatisEnable() {
        return lemonBuildMybatisEnable;
    }

    public static void setLemonBuildMybatisEnable(Boolean lemonBuildMybatisEnable) {
        LemonApplicationInitializer.lemonBuildMybatisEnable = lemonBuildMybatisEnable;
    }

    public static Boolean getLemonBuildRedisEnable() {
        return lemonBuildRedisEnable;
    }

    public static void setLemonBuildRedisEnable(Boolean lemonBuildRedisEnable) {
        LemonApplicationInitializer.lemonBuildRedisEnable = lemonBuildRedisEnable;
    }

    public static Boolean getLemonBuildRabbitMQEnable() {
        return lemonBuildRabbitMQEnable;
    }

    public static void setLemonBuildRabbitMQEnable(Boolean lemonBuildRabbitMQEnable) {
        LemonApplicationInitializer.lemonBuildRabbitMQEnable = lemonBuildRabbitMQEnable;
    }

    public static Boolean getLemonBuildInZkEnable() {
        return lemonBuildInZkEnable;
    }

    public static void setLemonBuildInZkEnable(Boolean lemonBuildInZkEnable) {
        LemonApplicationInitializer.lemonBuildInZkEnable = lemonBuildInZkEnable;
    }

    public static Boolean getLemonBuildInEsEnable() {
        return lemonBuildInEsEnable;
    }

    public static void setLemonBuildInEsEnable(Boolean lemonBuildInEsEnable) {
        LemonApplicationInitializer.lemonBuildInEsEnable = lemonBuildInEsEnable;
    }

    public static boolean isZkConnected() {
        return zkConnected;
    }

    public static void setZkConnected(boolean zkConnected) {
        LemonApplicationInitializer.zkConnected = zkConnected;
    }

    public static void setZkRegister(ZkRegister zkRegister) {
        LemonApplicationInitializer.zkRegister = zkRegister;
    }

    public static ZkRegister getZkRegister() {
        return zkRegister;
    }

    public static ServiceInfo getServiceInfo() {
        return serviceInfo;
    }

    public static void setServiceInfo(ServiceInfo serviceInfo) {
        LemonApplicationInitializer.serviceInfo = serviceInfo;
    }

    public static void notifyZkPropertyChange(String path, String changeData) {
        if (lemonZkPropertiesListener == null) {
            return;
        }
        lemonZkPropertiesListener.onPropertyChange(path, changeData);
    }

    public static boolean addZkPropertyKeyMapping(String key, String path) {
        if (lemonZkPropertiesListener == null) {
            lemonZkPropertiesListener = new LemonZkPropertiesListener();
        }
        return lemonZkPropertiesListener.putKeyPath(key, path);
    }

    public static boolean addZkPropertyListener(String key, ZkPropKeyListener listener) {
        if (lemonZkPropertiesListener == null) {
            return false;
        }
        lemonZkPropertiesListener.addListeningPath(key);
        lemonZkPropertiesListener.addListener(key, listener);
        return true;
    }
}
